/*
 * Copyright 2013 Robert Peszek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fpig.lists.functions

import fpig.util.CallUtil
import fpig.util.FunUtil;
import fpig.lists.structures.FunList

/**
 * Basic List functions
 *
 * http://code.google.com/p/fpiglet/
 * @author Robert Peszek
 */
class Base {
	/**
	 * foldl :: (a -> b -> a) -> a -> [b] -> a
	 * @return
	 */
	static Closure getFoldLC() {
		return  {Closure foldF, acc, FunList l->
			if(l.empty)
			    acc
		    else 
			    Base.foldLC(foldF, foldF(acc, l.head), l.tail) //Groovy call dispatch needs it with class name!
		}
	}
	static Closure foldL = CallUtil.toFunction foldLC
	
	static Closure reduceL = CallUtil.toFunction {Closure foldF, FunList l-> 
		 if(l.empty)
		    null //TODO change this to maybe
	     else 
		    Base.foldLC(foldF, l.head, l.tail) //can use non-converted closure all params passed
			                                   //Groovy call dispatch needs it with class name!
	}

	static Closure getFoldRC() {
		return  {Closure foldF, acc, FunList l->
			//l.empty ? acc: l.build(l.head,  foldF(l.getTail(), acc) ) 
			if(l.empty)
			   acc
			else {
				foldF(l.head, Base.foldRC(foldF, acc, l.tail)) //Haskell knows that foldF does not depend on second parameter, Groovy does not
			}
			   
		}
	}
	/**
	 * Version of fold which for finite lists
	 * foldR :: (b -> a -> a) -> a -> [b] -> a
	 */
	static Closure foldR = CallUtil.toFunction foldRC

	static Closure reduceR = CallUtil.toFunction {Closure foldF, FunList l->
		if(l.empty)
		   null //TODO change this to maybe
		else
		   Base.foldRC(foldF, l.head, l.tail) //can use non-converted closure all params passed
											  //Groovy call dispatch needs it with class name!
    }

	/**
	* For infinite lists
	* The trick is that the accumulator needs to be a closure.
	* foldXr :: foldR :: (b -> (_->a) -> a) -> a -> [b] -> a
	*/
	static Closure getFoldXRC() {
		return  {Closure foldF, acc, FunList l->
			if(l.empty)
			   acc
			else {
			    foldF(l.head, {Base.foldXRC(foldF, acc, l.tail)}) //Haskell knows that foldF does not depend on second parameter, Groovy does not
			}
			   
		}
	}
	/**
	 * Version of fold which for finite lists
	 */
	static Closure foldXR = CallUtil.toFunction foldXRC

	/**
	 * version that works on infinity
	 */
	static Closure reduceXR = CallUtil.toFunction {Closure foldF, FunList l->
		if(l.empty)
		   null //TODO change this to maybe
		else
		   Base.foldXRC(foldF, l.head, l.tail) //can use non-converted closure all params passed
											  //Groovy call dispatch needs it with class name!
	}

	
	static Closure getFilterC() {
		return {Closure predicate,  FunList l ->
			if(l.empty)
				l
			else if (predicate(l.head))
				l.build(l.head, { Base.filterC(predicate, l.getTail()) }) //Groovy call dispatch needs it with class name!
			else
				Base.filterC(predicate, l.getTail()) //Groovy call dispatch needs it with class name!
		}
	}
	static Closure filter = CallUtil.toFunction filterC


	static Closure getMapC() {
		return {Closure expr, FunList l ->
			l.empty ? l: l.build(expr(l.head), { Base.mapC(expr, l.getTail())})
		}
	}
	static Closure map = CallUtil.toFunction mapC

	static Closure prepend = CallUtil.toFunction {a, FunList l -> 	
		l.build(a, { l })
	}

	//easily derived
	/** 
	 * Cute element function supporting prepending with <<
	 */
	static Closure e = prepend
	
	static Closure reverse = foldL(CallUtil.flip(prepend), FunList.EMPTYLIST)
}
